//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

// Game duration in secs, no limit if the duration is set to 0
$Game::Duration = 0;

// When a client score reaches this value, the game is ended.
$Game::EndGameScore = -3;

// Pause while looking over the end game screen (in secs)
$Game::EndGamePause = 1; // was 10; keep it here so we have an option to close.

$Game::DisplayEnemyNames = false;


//-----------------------------------------------------------------------------
//  Functions that implement game-play
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function onServerCreated()
{
   // Server::GameType is sent to the master server.  This variable should uniquely identify your game and/or mod.
   $Server::GameType = "Mission: Serra";

   // Server::MissionType sent to the master server.  Clients can filter servers based on mission type.
   $Server::MissionType = "Single Player";

   // GameStartTime is the sim time the game started. Used to calculated game elapsed time.
   $Game::StartTime = 0;

   // Load up all datablocks, objects etc.  This function is called when
   // a server is constructed.
   exec("./audioProfiles.cs");
   exec("./envAudioProfiles.cs");
   exec("./camera.cs");
   exec("./markers.cs"); 
   exec("./triggers.cs"); 
   
   exec("./shapeBase.cs");
   exec("./inventory.cs");
   exec("./item.cs");
   exec("./health.cs");
   exec("./staticShape.cs");
   
   exec("common/server/lightingSystem.cs");
   exec("./particles.cs");  
   
   exec("./radiusDamage.cs");
   exec("./weapons/weapon.cs");
   exec("./weapons/crossbow.cs");
   exec("./weapons/rocket_pistol.cs");
   exec("./weapons/rocket_launcher.cs");
   exec("./weapons/grenade_launcher.cs");
   exec("./weapons/hand_grenade.cs");
   exec("./weapons/mac10.cs");
   
   exec("./ai/player.cs");
   exec("./ai/aiManager.cs");
   exec("./ai/aiPlayer.cs");
   exec("./ai/aiEnemy.cs");
   exec("./ai/aiDead.cs"); 
   exec("./ai/aiFriendly.cs");
   //exec("./ai/guard.cs"); 
   
   exec("./interactive/destroyable.cs");
   exec("./interactive/power_crystal.cs");
   exec("./interactive/jailBar.cs");
   exec("./interactive/key.cs");  
   exec("./interactive/door.cs");  
   exec("./interactive/controls.cs");
   //exec("./sgExamples.cs");

   // Keep track of when the game started
   $Game::StartTime = $Sim::Time;
}

function onServerDestroyed()
{
   // This function is called as part of a server shutdown.
}


//-----------------------------------------------------------------------------

function onMissionLoaded()
{
   // Called by loadMission() once the mission is finished loading.
   // Nothing special for now, just start up the game play.
   startGame();
}

function onMissionEnded()
{
   // Called by endMission(), right before the mission is destroyed

   // Normally the game should be ended first before the next
   // mission is loaded, this is here in case loadMission has been
   // called directly.  The mission will be ended if the server
   // is destroyed, so we only need to cleanup here.
   cancel($Game::Schedule);
   $Game::Running = false;
   $Game::Cycling = false;
}


//-----------------------------------------------------------------------------

function startGame()
{
	if ($Game::Running) {
		error("startGame: End the game first!");
		return;
	}

	// Inform the client we're starting up
	for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
		%cl = ClientGroup.getObject( %clientIndex );
		commandToClient(%cl, 'GameStart');

		// Other client specific setup..
		%cl.score = 0;
	}
   
	// Start the game timer
	if ($Game::Duration)
		$Game::Schedule = schedule($Game::Duration * 1000, 0, "onGameDurationEnd" );
	$Game::Running = true;
	
	// Start the AIManager
	new ScriptObject(AIManager) {};
	MissionCleanup.add(AIManager);
	AIManager.schedule(500,"spawn");
	AIManager.think();
   
	//schedule(3000, 0, "CreateBots");
	schedule(200,0,"CreateNPC");
	schedule(100,0,"CreateDead");
	displayNames($Game::DisplayEnemyNames);

	// Reset the gui key displays
	red.setVisible(false);
	blue.setVisible(false);
	yellow.setVisible(false);

	// Reset the life indicator
	one.setVisible(false);
	two.setVisible(false);
	three.setVisible(true);

	$Game::Won = false;
	// Hide the chat frame
	OuterChatHud.setVisible(false);
}


/*
// When enter is pressed, check if we're inside a specific trigger area, and if we have a specific key
// If we do, call a function
function enterPressed(%val)
{  
   // When the button is pressed down...
   if (%val)
   {
      // If we're next to the jail door switch....
	  if ($jail_door_trigger)
	  {
	     %jail_wire_cover = nameToID("MissionGroup/jailmisc/wirecover");
		 
		 // First, remove the cover panel
	     if (%jail_wire_cover != -1)
		 {
		    messageAll('','Now I just have to cross these two wires...');
			%jail_wire_cover.delete();
		 }
		 // Then open the jail cell door
		 else
		 {
		     messageAll('','Hah!  Lock ME in my own cell, will ya!  All I need now is a gun.');
			  // Get the id of the SimGroup which holds the laser beams for our jail door
			  %jail_bar_group = nameToID("MissionGroup/jaillaserbars");
			  
			  // Fade out the bars, and hide them (so we can go through) once they're invisible
			  %jail_bars = %jail_bar_group.getCount();
			  for (%i = 0; %i < %jail_bars; %i++)
			  {
				%jail_bar_group.getObject(%i).startFade(1500, 0, true);
				%jail_bar_group.getObject(%i).schedule(1600, setHidden, true);
			  }
		  }
	  }
	  
	  // If we have the bridge door key, and we're next to the bridge door switch (have the trigger set a global variable when in/out of trigger)...
      if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Weapon_Key) && $pistol_weapon_trigger)
	  {
	     messageAll('','Yeehaw!  Now to blast my way to the bridge.');
		 %pistol_group = nameToID("MissionGroup/PistolGlasscase");
			  
		  // Fade out the bars, and hide them (so we can go through) once they're invisible
		  %group_items = %pistol_group.getCount();
		  for (%i = 0; %i < %group_items; %i++)
		  {
			  if (%pistol_group.getObject(%i).getName() $= "glasscase")
			  {
			     %pistol_group.getObject(%i).delete();
			  }
		  }
	  }
	  else if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Weapon_Key) < 1 && $pistol_weapon_trigger)
	  {
	     messageAll('','I need the weapons locker key.  I wonder if there\'s one near near the brig...');
	  }
	  
	  // If we have the bridge door key, and we're next to the bridge door switch (have the trigger set a global variable when in/out of trigger)...
      if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Weapon_Key) && $grenade_weapon_trigger)
	  {
	     messageAll('','I must be in heaven.');
		 %grenade_group = nameToID("MissionGroup/GrenadeGlasscase");
			  
		  // Fade out the bars, and hide them (so we can go through) once they're invisible
		  %group_items = %grenade_group.getCount();
		  for (%i = 0; %i < %group_items; %i++)
		  {
			  if (%grenade_group.getObject(%i).getName() $= "glasscase")
			  {
			     %grenade_group.getObject(%i).delete();
			  }
		  }
	  }
	  else if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Weapon_Key) < 1 && $grenade_weapon_trigger)
	  {
	     messageAll('','I need the weapons locker key.  I wonder if there\'s one near near the brig...');
	  }
	  
	  // If we have the bridge computer key, and we're next to the bridge computer (have the trigger set a global variable when in/out of the trigger)...
      if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Bridge_Access_Key) && $bridge_door_trigger)
	  {
		 %door_id = nameToId("bridge_door");
		 %door_id.doorLocked = false;
		 messageAll('','Time to pry the computer control key from this guy\'s cold, dead hands.');
	  }
	  else if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Bridge_Access_Key) < 1 && $bridge_door_trigger)
	  {
	     messageAll('','I need to find the bridge access key before I can in.');
	  }
	 
	  // If we have the bridge computer key, and we're next to the bridge computer (have the trigger set a global variable when in/out of the trigger)...
      if (isObject(LocalClientConnection.player) && LocalClientConnection.player.getInventory(Bridge_Computer_Key) && $bridge_computer_trigger)
	  {
		 $Game::Won = true;
		 onCycleExec();
	  }
   }
}
*/

function endGame()
{
	if (!$Game::Running)  {
		error("endGame: No game running!");
		return;
	}

	// Stop the AIManager
	AIManager.delete();

	// Stop any game timers
	cancel($Game::Schedule);

	// Inform the client the game is over
	for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
		%cl = ClientGroup.getObject( %clientIndex );
		commandToClient(%cl, 'GameEnd');
	}

	// Delete all the temporary mission objects
	resetMission();
	$Game::Running = false;
}

function onGameDurationEnd()
{
	// This "redirect" is here so that we can abort the game cycle if
	// the $Game::Duration variable has been cleared, without having
	// to have a function to cancel the schedule.
	if ($Game::Duration && !isObject(EditorGui))
		cycleGame();
}


//-----------------------------------------------------------------------------

function cycleGame()
{
	// This is setup as a schedule so that this function can be called
	// directly from object callbacks.  Object callbacks have to be
	// carefull about invoking server functions that could cause
	// their object to be deleted.
	if (!$Game::Cycling) {
		$Game::Cycling = true;
		$Game::Schedule = schedule(0, 0, "onCyclePauseEnd");
	}
}

function onCycleExec()
{
	// End the current game and start another one, we'll pause for a little
	// so the end game victory screen can be examined by the clients.
	endGame();
	$Game::Schedule = schedule($Game::EndGamePause * 1, 0, "onCyclePauseEnd");
}

function onCyclePauseEnd()
{
	endGame();
	$Game::Cycling = false;

	// Just cycle through the missions for now.
	%search = $Server::MissionFileSpec;
	for (%file = findFirstFile(%search); %file !$= ""; %file = findNextFile(%search)) 
	{
		if (%file $= $Server::MissionFile) 
		{
			// Get the next one, back to the first if there is no next.
			%file = findNextFile(%search);
			if (%file $= "") 
			{
				// Here we just want to end the game. What happens if we set the canvas to a GUI?
				if ($Game::Won == true)
				{
					Canvas.setContent(WinGui);
				}
				else
				{
					Canvas.setContent(LoseGui);
				}
			}
			break;
		}
	}
	if (%file != "") 
	{
		loadMission(%file);
	}
}


//-----------------------------------------------------------------------------
// GameConnection Methods
// These methods are extensions to the GameConnection class. Extending
// GameConnection make is easier to deal with some of this functionality,
// but these could also be implemented as stand-alone functions.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function GameConnection::onClientEnterGame(%this)
{
	commandToClient(%this, 'SyncClock', $Sim::Time - $Game::StartTime);

	// Create a new camera object.
	%this.camera = new Camera() {
		dataBlock = Observer;
	};
	MissionCleanup.add( %this.camera );
	%this.camera.scopeToClient(%this);

	// Setup game parameters, the onConnect method currently starts
	// everyone with a 0 score.
	%this.score = 0;

	// Create a player object.
	%this.spawnPlayer();
}

function GameConnection::onClientLeaveGame(%this)
{
   if (isObject(%this.camera))
      %this.camera.delete();
   if (isObject(%this.player))
      %this.player.delete();
}


//-----------------------------------------------------------------------------

function GameConnection::onLeaveMissionArea(%this)
{
	// The control objects invoked this method when they
	// move out of the mission area.
}

function GameConnection::onEnterMissionArea(%this)
{
	// The control objects invoked this method when they
	// move back into the mission area.
}


//-----------------------------------------------------------------------------

function GameConnection::onDeath(%this, %sourceObject, %sourceClient, %damageType, %damLoc)
{
	// Clear out the name on the corpse
	%this.player.setShapeName("");

	// Switch the client over to the death cam and unhook the player object.
	if (isObject(%this.camera) && isObject(%this.player)) {
		%this.camera.setMode("Corpse",%this.player);
		%this.setControlObject(%this.camera);
	}
	%this.player = 0;

	// Doll out points and display an appropriate message
	if (%damageType $= "Suicide" || %sourceClient == %this) 
	{
		%this.incScore(-1);
		messageAll('MsgClientKilled',%this.name @ ' takes his own life!');
		if (%this.score <= $Game::EndGameScore)
		{
			cycleGame();
		}
	}
	else 
	{
		%this.incScore(-1);
		messageAll('MsgClientKilled',%this.name @ ' gets nailed by ' @ %sourceClient.name);
		if (%this.score <= $Game::EndGameScore)
		{
			cycleGame();
		}
		else 
		{
			// TODO: We should get rid of all this stuff
			// and in the createPlayer() code, we should check to see if the gui icons are
			// visible, and if they are then we'll spawn the items into their inventory.  We probably won't need the
			// $etc_etc_trigger globals anymore, but if we do, scope them to the $Game object or something else
			$pistol_weapon_trigger = false;
			$grenade_weapon_trigger = false;
			$bridge_door_trigger = false;
			$bridge_computer_trigger = false;
			red.setVisible(false);
			blue.setVisible(false);
			yellow.setVisible(false);

			// TODO: Stop using the score to hold the player's lives
			if (%this.score == -1) 
			{
				three.setVisible(false);
				two.setVisible(true);
				one.setVisible(false);
			}
			else if (%this.score == -2) 
			{
				three.setVisible(false);
				two.setVisible(false);
				one.setVisible(true);
			}
		}
	}
}


//-----------------------------------------------------------------------------

function GameConnection::spawnPlayer(%this)
{
	// Combination create player and drop him somewhere
	%spawnPoint = pickSpawnPoint();
	%this.createPlayer(%spawnPoint);
}   


//-----------------------------------------------------------------------------

function GameConnection::createPlayer(%this, %spawnPoint)
{
	if (%this.player > 0)  {
		// The client should not have a player currently
		// assigned.  Assigning a new one could result in 
		// a player ghost.
		error( "Attempting to create an angus ghost!" );
	}

	// Create the player object
	%player = new Player() {
		dataBlock = PlayerBody;
		client = %this;
	};
	//%player.setSkinName("base_red");
	//%player.getDataBlock().emap = false;
	MissionCleanup.add(%player);

	// Player setup...
	%player.setTransform(%spawnPoint);
	%player.setShapeName(%this.name);

	// Starting equipment
	// TODO: don't give them any ammo/clips by default
	%player.setInventory(CrossbowAmmo,99999);
	%player.setInventory(RocketLauncherAmmo,99999);
	%player.setInventory(GrenadeLauncherAmmo,99999);
	%player.setInventory(mac10AmmoClip,30);
	%player.setInventory(mac10Ammo,30);
	%player.setInventory(RocketPistolAmmo,99999);

	/*
	if (blue.isVisible())
		%player.setInventory(Weapon_Key, 1);
	if (yellow.isVisible())
		%player.setInventory(Bridge_Access_Key, 1);
	if (red.isVisible())
		%player.setInventory(Bridge_Computer_Key, 1);
	*/

	// Update the camera to start with the player
	%this.camera.setTransform(%player.getEyeTransform());

	// Give the client control of the player
	%this.player = %player;
	%this.setControlObject(%player);
}


//-----------------------------------------------------------------------------
// Support functions
//-----------------------------------------------------------------------------

function pickSpawnPoint() 
{
	%groupName = "MissionGroup/PlayerDropPoints";
	%group = nameToID(%groupName);

	if (%group != -1) {
		%count = %group.getCount();
		if (%count != 0) {
			%index = getRandom(%count-1);
			%spawn = %group.getObject(%index);
			return %spawn.getTransform();
		}
		else
			error("No spawn points found in " @ %groupName);
	}
	else
		error("Missing spawn points group " @ %groupName);

	// Could be no spawn points, in which case we'll stick the
	// player at the center of the world.
	return "0 0 300 1 0 0 0";
}

// Toggle display of player names on/off
function toggleNames(%val)
{
	$Game::DisplayEnemyNames = !$Game::DisplayEnemyNames;
	displayNames($Game::DisplayEnemyNames);
}

// Turn display of player names on/off
function displayNames(%val)
{
	$Game::DisplayEnemyNames = %val;
	for(%i = 0;%i < PlayGui.getCount(); %i++)
	{
		%obj = PlayGui.getObject(%i);
		if(%obj.getClassName() $= "GuiShapeNameHud")
		{
			%obj.visible = %val;
			return;
		}
	}
}